﻿using System;

namespace Simp.CodeModel.CoreFunctions
{
	/// <summary>Base class for function taking a left and right functions (e.g. addition).</summary>
	public abstract class BinaryFunctionBase : IFunction, IFunctionSerializer
	{
		private static readonly string[] PARAMETER_NAMES = new string[] { "left", "right" };

		/// <summary>Returns the result of binary operation performed on integers.</summary>
		/// <remarks>Can return <c>null</c> if the operation should remain unresolved.</remarks>
		/// <param name="a"/>
		/// <param name="b"/>
		/// <returns/>
		protected abstract IFunction IntegerOperation(int a, int b);

		/// <summary>Returns the result of binary operation performed on rationals.</summary>
		/// <remarks>Can return <c>null</c> if the operation should remain unresolved.</remarks>
		/// <param name="a"/>
		/// <param name="b"/>
		/// <returns/>
		protected abstract IFunction RationalOperation(RationalFunction a, RationalFunction b);

		/// <summary>Returns the binary operation to perform on reals.</summary>
		/// <remarks>Can return <c>null</c> if the operation should remain unresolved.</remarks>
		/// <param name="a"/>
		/// <param name="b"/>
		/// <returns/>
		protected abstract IFunction RealOperation(double a, double b);

		/// <summary>Returns the symbol of the operation.</summary>
		/// <remarks>Used to implement <see cref="IFunctionSerializer"/>.</remarks>
		protected abstract string OperationSymbol { get; }

		#region IFunction Members
		/// <summary>See <see cref="IFunction.FunctionName"/>.  Implemented by derived class.</summary>
		public abstract string FunctionName { get; }

		/// <summary>See <see cref="IFunction.ParameterNames"/>.</summary>
		public string[] ParameterNames
		{
			get { return PARAMETER_NAMES; }
		}

		/// <summary>Returns an empty list:  no defaults.</summary>
		public IFunctionParameterBinding[] DefaultParameterValues
		{
			get { return FunctionHelper.EmptyBindings; }
		}

		/// <summary>Nothing to visit.</summary>
		/// <param name="visitor"></param>
		/// <returns></returns>
		public IFunction AcceptVisitor(IFunctionVisitor visitor)
		{
			return null;
		}

		/// <summary>Simplifies the binary operations.</summary>
		/// <param name="simplificationMode"/>
		/// <param name="parameters"></param>
		/// <returns></returns>
		public virtual IFunction Simplify(SimplificationMode simplificationMode, params IFunction[] parameters)
		{
			FunctionHelper.ValidateParameters(this, parameters);

			INumericFunction left = parameters[0] as INumericFunction;
			INumericFunction right = parameters[1] as INumericFunction;

			if (left == null || right == null)
			{
				return null;
			}
			else
			{
				IntegerFunction leftInteger = left.GetInteger();
				IntegerFunction rightInteger = right.GetInteger();

				if (leftInteger == null || rightInteger == null)
				{
					RationalFunction leftRational = left.GetRational();
					RationalFunction rightRational = right.GetRational();

					if (leftRational == null || rightRational == null)
					{
						RealFunction leftReal = left.GetReal(simplificationMode == SimplificationMode.AllowApproximation);
						RealFunction rightReal = right.GetReal(simplificationMode == SimplificationMode.AllowApproximation);

						if (leftReal == null || rightReal == null)
						{
							return null;
						}
						else
						{
							return RealOperation(leftReal.Value, rightReal.Value);
						}
					}
					else
					{
						return RationalOperation(leftRational, rightRational);
					}
				}
				else
				{
					return IntegerOperation(leftInteger.Value, rightInteger.Value);
				}
			}
		}
		#endregion

		#region IFunctionSerializer Members
		void IFunctionSerializer.Serialize(AppendOnlyStringBuilder builder, params IFunction[] parameters)
		{
			FunctionHelper.ValidateParameters(this, parameters);

			builder.Append("(");
			FunctionHelper.Serialize(parameters[0], builder);
			builder.Append(" ");
			builder.Append(OperationSymbol);
			builder.Append(" ");
			FunctionHelper.Serialize(parameters[1], builder);
			builder.Append(")");
		}
		#endregion
	}
}